<?php

// Namespace

namespace Loop;


/**
 * CPreferenceManager is a class that allows to manage preferences, i.e. set them and get their value in a secure way
 */

class CPreferenceManager
{
	// Properties
	
	private $environmentName = null;
	private $environmentUri = null;
	private $preferences = null;
	private $timeOfDeployment = null;
		
	
	/**
	 * Constructs a preference manager object
	 */
	
	public function __construct()
	{
		$this->preferences = array();
	}
	
	
	/**
	 * Gets the name of the current environment
	 *
	 * @return	string 	The name of the environment (e.g. Prod, Test, etc.)
	 */
	
	public function getEnvironmentName()
	{
		return $this->environmentName;
	}
	
	
	/**
	 * Gets a pre-defined preference and returns it
	 *
	 * @param	string	$preferencePath 	The path to the preference
	 *
	 * @return	array 	A reference to the preference
	 */
	
	private function getPreference($preferencePath)
	{
		// Have we defined this preference?
		
		if (isset($this->preferences[$preferencePath]) == false)
		{
			return;
		}
		
		
		// Get a reference to the preference
		
		$preference = &$this->preferences[$preferencePath];
		
		
		return $preference;
	}


	/**
	 * Returns all the preferences
	 *
	 * @return	array 	An array which contains all the preferences
	 */

	public function getPreferences()
	{
		return $this->preferences;
	}
	
	
	/**
	 * Gets a pre-defined preference and returns its value
	 *
	 * @param	string	$preferencePath 	The path to the preference
	 *
	 * @return	mixed 	The value of the preference
	 */
	
	public function getPreferenceValue($preferencePath)
	{
		// Have we defined this preference?
		
		$preference = $this->getPreference($preferencePath);
		

		if (isset($preference['value']) == false)
		{
			return;
		}
		
		
		return $preference['value'];
	}
	
	
	/**
	 * Gets the latest time of deployment
	 *
	 * @return	int 	The latest time of deployment (a timestamp)
	 */
	
	public function getTimeOfDeployment()
	{
		// We need a time of deployment

		if (is_null($this->timeOfDeployment) === true)
		{
			// We need to know whether the application has been deployed

			$pathToDeployment = path
			(
				array
				(
					PATH_APPLICATION,
					'Data',
					'Preference'
				),
				false,
				false
			);

			$pathToDeployment .= 'Deployment.txt';

			if (file_exists($pathToDeployment) == true)
			{
				$this->timeOfDeployment = file_get_contents($pathToDeployment);
			}
			else
			{
				$this->timeOfDeployment = 0;
			}
		}


		return $this->timeOfDeployment;
	}
	
	
	/**
	 * Initializes the preference manager
	 *
	 * @param	array	$pathsToExtensions 	Paths to extensions
	 */
	
	public function initialize($pathsToExtensions = null)
	{
		// Are we running in command line?
		
		if (app()->isRunningCli() == true)
		{
			// Get the environment from the command line
			
			$this->environmentName = fv('env', true);
		}
		

		// Get paths of extensions + application
		
		if (empty($pathsToExtensions) == true)
		{
			$pathsToExtensions = app()->getExtensionManager()->getPathsToExtensions();
		}
		
		
		// Do we have a developer profile?
		
		$pathToProfile = PATH_APPLICATION . 'Preference/Profile.txt';
		
		if (file_exists($pathToProfile) == true)
		{
			$this->profileName = file_get_contents($pathToProfile);
		}
		else
		{
			$this->profileName = null;
		}
		
		
		// Define the number of passes
		
		$nbPasses = count($pathsToExtensions) - 1;
		
		
		// Perform n passes
		
		for ($i = 0; $i < $nbPasses; $i++)
		{
			// Load main preferences first
			
			foreach ($pathsToExtensions as $pathToExtension)
			{
				$this->loadPreferences($pathToExtension, false);
			}
			
			
			// Do we have any environment defined?
			
			$environments = pref('[Loop]/deployment/environments');

			if (is_array($environments) == false)
			{
				continue;
			}
			
			
			// We can now load preferences related to current environment
			
			if (empty($this->environmentName) == true)
			{
				foreach ($environments as $environmentName => $environmentUri)
				{
					// If preferences are not for the current server, skip
					
					if (app()->isEnvironmentUri($environmentUri) == false)
					{
						continue;
					}
					
					
					// Store the environment
					
					$this->environmentName = $environmentName;
					$this->environmentUri = $environmentUri;
					
					
					// We're done
					
					break;
				}
			}
			
			
			// Parse all extensions for environment-specific preferences
			
			foreach ($pathsToExtensions as $pathToExtension)
			{
				$this->loadPreferences($pathToExtension, true);
			}
			
			
			// Make sure we remove the first extension, this ensures we progressively eliminate extensions as passes go
			
			array_shift($pathsToExtensions);
		}
	}
	

	/**
	 * Loads the preferences for a given extension and an optional environment
	 *
	 * @param	string	$pathToExtension 		The path to the extension
	 * @param	string	$useEnvironmentName 	The environment name
	 */

	public function loadPreferences($pathToExtension, $useEnvironmentName = false)
	{
		// Prepare standard path

		$pathToPreference = $pathToExtension . 'Preference/Preferences';


		// Add environment if necessary

		if ($useEnvironmentName === true)
		{
			$pathToPreference .= '.' . $this->environmentName;
		}


		// Add universe, if any

		$universe = pref('[Loop]/application/universe');

		if (empty($universe) === false)
		{
			// Load Preferences.Environment.Universe.php
			
			if (file_exists($pathToPreference . '.' . $universe . '.php') === true)
			{
				require($pathToPreference . '.' . $universe . '.php');
			}
		}
		
		
		// Load Preferences.Environment.php
	
		if (file_exists($pathToPreference . '.php') === true)
		{
			require($pathToPreference . '.php');
		}	
		
		
		// Use profile, if any
		
		if (empty($this->profileName) === false)
		{
			if (empty($universe) === false)
			{
				// Load Preferences.Environment.Universe.Profile.php

				$pathToPreference .= '.' . $universe . '.' . $this->profileName . '.php';
				
			}
			else
			{
				// Load Preferences.Environment.Profile.php

				$pathToPreference .= '.' . $this->profileName . '.php';
			}

			if (file_exists($pathToPreference) == true)
			{
				require($pathToPreference);
			}
		}
	}


	/**
	 * Sets the environment
	 *
	 * @param	string 	$environmentName 	The name of the environment to be set
	 */

	public function setEnvironmentName($environmentName)
	{
		$environments = pref('[Loop]/deployment/environments');

		if 
		(
			(is_array($environments) == true) &&
			(array_key_exists($environmentName, $environments) == false)
		)
		{
			e
			(
				EXCEPTION_APPLICATION_ENVIRONMENT_NOT_DEFINED,
				array
				(
					'environment name' => $environmentName
				)
			);
		}

		$this->environmentName = $environmentName;
	}
	
	
	/**
	 * Sets a preference
	 *
	 * @param	string 	$preferencePath		The path to the preference
	 * @param	mixed	$preferenceValue	The value of the preference
	 * @param	bool	$isLocked			True to lock the preference, false otherwise
	 */
	
	public function setPreferenceValue($preferencePath, $preferenceValue, $isLocked = false)
	{
		// Is the preference locked?
		
		$preference = $this->getPreference($preferencePath);
		
		if
		(
			(isset($preference['isLocked']) === true) &&
			($preference['isLocked'] === true)
		)
		{
			return;
		}


		// Handle PRE special cases

		if ($preferencePath === '[Loop]/localization/languageIsoCode')
		{
			// Check that language is supported
			
			if (in_array($preferenceValue, pref('[Loop]/localization/languages')) == false)
			{
				if (in_array(pref('[Loop]/localization/defaultLanguageIsoCode'), pref('[Loop]/localization/languages')) === false)
				{
					e
					(
						EXCEPTION_LANGUAGE_NOT_FOUND,
						array('languageIsoCode', $preferenceValue)
					);
				}

				pref('[Loop]/localization/languageIsoCode', $preferenceValue);				
			}
			
			setlocale(LC_ALL, $preferenceValue);
			
			v(pref('[Loop]/authentication/source') . '::previousLanguageIsoCode', pref('[Loop]/localization/languageIsoCode'));
		}
		else if ($preferencePath === '[Loop]/localization/timezone')
		{
			date_default_timezone_set($preferenceValue);
		}
		else if (preg_match('/^\[[a-zA-Z\-]+\]\/database\/(login|name)$/', $preferencePath, $matches) === 1)
		{
			// Add the universe

			$universe = strtolower(pref('[Loop]/application/universe'));


			if
			(
				(empty($universe) === false) &&
				(strpos($preferenceValue, $universe) !== 0)
			)
			{
				$preferenceValue = $universe . '_' . $preferenceValue;
			}


			// Ensure it's in lowercase, without spaces

			$preferenceValue = trim(strtolower($preferenceValue));


			// Make sure it fits the max length

			$preferenceValue = substr
			(
				$preferenceValue,
				0,
				pref('[Loop]/database/' . $matches[1] . '/maxLength')
			);		
		}

		
		// Set the preference value
		
		$this->preferences[$preferencePath] = array();
		$this->preferences[$preferencePath]['value'] = $preferenceValue;
		$this->preferences[$preferencePath]['isLocked'] = $isLocked;


		// Handle POST special cases

		if ($preferencePath === '[Loop]/application/universe')
		{
			if (empty($preferenceValue) === false)
			{
				$this->initialize();
			}
		}
	}
}

?>
